package cn.mujiankeji.mbrowser.功能.下载.引擎

import android.util.Log
import cn.mujiankeji.mbrowser.功能.下载.工具.M3U8解析器
import cn.mujiankeji.mbrowser.功能.下载.模型.下载任务
import cn.mujiankeji.mbrowser.功能.下载.模型.下载状态
import io.ktor.client.HttpClient
import io.ktor.client.call.body
import io.ktor.client.request.get
import io.ktor.client.statement.HttpResponse
import io.ktor.client.statement.bodyAsChannel
import io.ktor.http.isSuccess
import io.ktor.utils.io.readAvailable
import kotlinx.coroutines.CancellationException
import kotlinx.coroutines.currentCoroutineContext
import kotlinx.coroutines.ensureActive
import kotlinx.coroutines.flow.MutableStateFlow
import java.io.File
import java.io.FileInputStream
import java.io.FileOutputStream
// JCA Imports for Decryption
import javax.crypto.Cipher
import javax.crypto.spec.IvParameterSpec
import javax.crypto.spec.SecretKeySpec
// import java.security.MessageDigest // Potentially for IV generation if not full length
// import kotlin.experimental.xor // Not standard, be careful with this.

class M3u8DownloadEngine(httpClient: HttpClient,
    private val m3u8解析器: M3U8解析器
) : Ktor下载引擎基础(httpClient) {

    private val TAG = "M3u8DownloadEngine"

    private suspend fun downloadAndParsePlaylist(
        playlistUrl: String,
        originalTaskUrl: String
    ): M3U8解析器.M3U8Playlist {
        Log.d(TAG, "Downloading M3U8 playlist from: $playlistUrl")
        val m3u8Content: String = httpClient.get(playlistUrl).body()
        Log.d(TAG, "Downloaded M3U8 content from $playlistUrl:\n$m3u8Content")
        return m3u8解析器.parse(m3u8Content, playlistUrl)
    }

    // AES-128解密函数
    private fun decryptFileAes128(
        inputFile: File,
        keyBytes: ByteArray,
        ivHexString: String?
    ): Boolean {
        Log.d(TAG, "准备解密文件: ${inputFile.name} using AES-128. IV: $ivHexString")
        if (!inputFile.exists() || inputFile.length() == 0L) {
            Log.e(TAG, "解密失败: 输入文件不存在或为空 ${inputFile.name}")
            return false
        }
        if (keyBytes.size != 16) { // AES-128 key must be 16 bytes
            Log.e(TAG, "解密失败: 无效的密钥长度 ${keyBytes.size} bytes for AES-128. 文件: ${inputFile.name}")
            return false
        }

        val tempDecryptedFile = File(inputFile.parentFile, inputFile.name + ".dec_tmp")

        try {
            val secretKeySpec = SecretKeySpec(keyBytes, "AES")
            val cipher = Cipher.getInstance("AES/CBC/PKCS7Padding") // Standard padding

            val ivBytes: ByteArray = if (ivHexString != null) {
                val hex = ivHexString.removePrefix("0x")
                if (hex.length != 32) {
                     Log.e(TAG, "无效的IV格式: $ivHexString. 需要16字节的16进制表示。")
                    return false
                }
                hex.chunked(2).map { it.toInt(16).toByte() }.toByteArray()
            } else {
                Log.w(TAG, "AES-128解密需要IV，但M3U8中未提供显式IV。文件: ${inputFile.name}")
                return false 
            }

            if (ivBytes.size != 16) { 
                 Log.e(TAG, "解密失败: IV 字节长度不是16 (${ivBytes.size} bytes). 文件: ${inputFile.name}")
                return false
            }

            cipher.init(Cipher.DECRYPT_MODE, secretKeySpec, IvParameterSpec(ivBytes))

            FileInputStream(inputFile).use { fis ->
                FileOutputStream(tempDecryptedFile).use { fos ->
                    val buffer = ByteArray(8192)
                    var bytesRead: Int
                    while (fis.read(buffer).also { bytesRead = it } != -1) {
                        val decryptedBytes = cipher.update(buffer, 0, bytesRead)
                        if (decryptedBytes != null) {
                            fos.write(decryptedBytes)
                        }
                    }
                    val finalBytes = cipher.doFinal()
                    if (finalBytes != null) {
                        fos.write(finalBytes)
                    }
                }
            }

            if (inputFile.delete()) {
                if (tempDecryptedFile.renameTo(inputFile)) {
                    Log.i(TAG, "文件 ${inputFile.name} 解密成功。")
                    return true
                } else {
                    Log.e(TAG, "解密后重命名临时文件失败: ${tempDecryptedFile.name} to ${inputFile.name}")
                    tempDecryptedFile.delete() 
                    return false
                }
            } else {
                Log.e(TAG, "删除原加密文件失败: ${inputFile.name}")
                tempDecryptedFile.delete() 
                return false
            }

        } catch (e: Exception) {
            Log.e(TAG, "AES-128解密过程中发生错误 for file ${inputFile.name}", e)
            tempDecryptedFile.delete() 
            return false
        }
    }


    private suspend fun downloadSegment(
        segmentUrl: String,
        outputFile: File,
        segmentKeyInfo: M3U8解析器.M3U8KeyInfo?,
        globalKeyInfo: M3U8解析器.M3U8KeyInfo?,
        keyBytes: ByteArray?
    ): Boolean {
        Log.d(TAG, "开始下载分片: $segmentUrl -> ${outputFile.name}")
        try {
            val response: HttpResponse = httpClient.get(segmentUrl)
            if (response.status.isSuccess()) {
                val byteReadChannel = response.bodyAsChannel()
                FileOutputStream(outputFile).use { outputStream ->
                    val buffer = ByteArray(8192)
                    while (!byteReadChannel.isClosedForRead) {
                        val bytesRead = byteReadChannel.readAvailable(buffer, 0, buffer.size)
                        if (bytesRead > 0) {
                            outputStream.write(buffer, 0, bytesRead)
                        }
                    }
                }

                val effectiveKeyToUse = segmentKeyInfo ?: globalKeyInfo
                if (effectiveKeyToUse != null && effectiveKeyToUse.method == "AES-128" && keyBytes != null) {
                    Log.d(TAG, "需要对分片 ${outputFile.name} 进行AES-128解密。Key URI: ${effectiveKeyToUse.uri}, IV: ${effectiveKeyToUse.iv}")
                    val decrypted = decryptFileAes128(outputFile, keyBytes, effectiveKeyToUse.iv)
                    if (!decrypted) {
                        Log.e(TAG, "分片 ${outputFile.name} 解密失败。")
                        outputFile.delete() 
                        return false 
                    }
                    Log.i(TAG, "分片 ${outputFile.name} 解密成功。")
                }

                Log.d(TAG, "分片处理成功 (下载及可能的解密): ${outputFile.name}")
                return true
            } else {
                Log.e(TAG, "分片下载失败: $segmentUrl, HTTP状态: ${response.status}")
                return false
            }
        } catch (e: CancellationException) {
            Log.i(TAG, "分片下载被取消: $segmentUrl", e)
            outputFile.delete()
            throw e
        } catch (e: Exception) {
            Log.e(TAG, "分片下载或处理异常: $segmentUrl", e)
            outputFile.delete()
            return false
        }
    }

    private suspend fun mergeSegments(
        segmentFiles: List<File>,
        outputFile: File
    ) {
        Log.i(TAG, "开始合并 ${segmentFiles.size} 个分片到 ${outputFile.name}")
        if (outputFile.exists()) {
            outputFile.delete()
        }
        FileOutputStream(outputFile, true).use { fos ->
            segmentFiles.forEachIndexed { index, segmentFile ->
                currentCoroutineContext().ensureActive()
                if (segmentFile.exists()) {
                    FileInputStream(segmentFile).use { fis ->
                        fis.copyTo(fos)
                        Log.d(TAG, "已合并分片 ${index + 1}/${segmentFiles.size}: ${segmentFile.name}")
                    }
                } else {
                    Log.e(TAG, "合并失败：分片文件 ${segmentFile.name} 未找到！")
                    throw RuntimeException("合并失败：分片文件 ${segmentFile.name} 未找到！")
                }
            }
        }
        Log.i(TAG, "所有分片已成功合并到: ${outputFile.absolutePath}")
    }


    override suspend fun 执行下载(
        任务: 下载任务,
        任务状态流: MutableStateFlow<下载任务>
    ) {
        var currentTask = 任务.copy(状态 = 下载状态.正在下载)
        任务状态流.value = currentTask
        Log.d(TAG, "开始M3U8下载: ${currentTask.文件名} (URL: ${currentTask.url})")

        val segmentsDir = File(currentTask.目标文件夹, ".${currentTask.文件名}_segments")
        if (!segmentsDir.exists()) {
            segmentsDir.mkdirs()
        }

        var m3u8Playlist: M3U8解析器.M3U8Playlist? = null
        val downloadedSegmentFiles = mutableListOf<File>()
        var downloadedKeyBytes: ByteArray? = null

        try {
            Log.d(TAG, "步骤1&2: 下载并解析M3U8索引文件 ${currentTask.url}")
            var initialPlaylist = downloadAndParsePlaylist(currentTask.url, currentTask.url)

            if (initialPlaylist.isMasterPlaylist) {
                Log.i(TAG, "检测到主播放列表 (Master Playlist) for ${currentTask.文件名}")
                if (initialPlaylist.streamInfos.isEmpty()) {
                    throw IllegalStateException("主播放列表不包含任何码流信息 (Stream Infos)")
                }
                val selectedStream = initialPlaylist.streamInfos.first()
                Log.i(TAG, "选择码流: ${selectedStream.uri} (带宽: ${selectedStream.bandwidth})")
                m3u8Playlist = downloadAndParsePlaylist(selectedStream.uri, currentTask.url)
                if (m3u8Playlist.isMasterPlaylist) {
                     throw IllegalStateException("选择的码流 (${selectedStream.uri}) 依然是一个主播放列表。")
                }
            } else {
                m3u8Playlist = initialPlaylist
            }

            if (m3u8Playlist == null || m3u8Playlist.segments.isEmpty()) {
                throw IllegalStateException("M3U8解析失败或媒体播放列表不包含任何分片")
            }

            Log.i(TAG, "M3U8媒体播放列表解析成功: ${m3u8Playlist.segments.size} 个分片 for ${currentTask.文件名}")
            val tsSegments = m3u8Playlist.segments
            currentTask = currentTask.copy(文件大小 = tsSegments.size.toLong())
            任务状态流.value = currentTask

            val globalEncryptionKeyInfo = m3u8Playlist.globalKeyInfo
            if (globalEncryptionKeyInfo != null && globalEncryptionKeyInfo.method == "AES-128" && !globalEncryptionKeyInfo.uri.isNullOrEmpty()) {
                Log.d(TAG, "步骤3: 检测到全局加密密钥 ${globalEncryptionKeyInfo.uri}, 方法: ${globalEncryptionKeyInfo.method}")
                try {
                    downloadedKeyBytes = httpClient.get(globalEncryptionKeyInfo.uri).body()
                    Log.i(TAG, "全局加密密钥下载成功 (URI: ${globalEncryptionKeyInfo.uri}, 大小: ${downloadedKeyBytes?.size ?: 0} bytes)")
                } catch (ke: Exception) {
                    Log.e(TAG, "下载全局加密密钥失败 (URI: ${globalEncryptionKeyInfo.uri})", ke)
                    throw RuntimeException("下载全局加密密钥失败: ${ke.message}", ke)
                }
            } else if (globalEncryptionKeyInfo != null && globalEncryptionKeyInfo.method == "AES-128" && globalEncryptionKeyInfo.uri.isNullOrEmpty()) {
                Log.w(TAG, "全局加密密钥方法为AES-128但没有URI，可能密钥已内嵌或通过其他方式提供，当前不支持")
            }

            Log.d(TAG, "步骤4: 开始下载TS分片 (共 ${tsSegments.size} 个)")
            var downloadedSegmentsCount = 0
            for ((index, segment) in tsSegments.withIndex()) {
                currentCoroutineContext().ensureActive()
                val segmentFileName = "segment_${String.format("%05d", index)}.ts"
                val segmentFile = File(segmentsDir, segmentFileName)

                var segmentSpecificKeyBytes: ByteArray? = null
                val segmentKeyInfo = segment.keyInfo
                val effectiveKeyInfoForSegment = segmentKeyInfo ?: globalEncryptionKeyInfo

                if (segmentKeyInfo != null && segmentKeyInfo.method == "AES-128" && !segmentKeyInfo.uri.isNullOrEmpty()) {
                    if (globalEncryptionKeyInfo == null || segmentKeyInfo.uri != globalEncryptionKeyInfo.uri) {
                        Log.d(TAG, "步骤3 (分片级): 检测到分片特定加密密钥 ${segmentKeyInfo.uri}")
                        try {
                            segmentSpecificKeyBytes = httpClient.get(segmentKeyInfo.uri).body()
                            Log.i(TAG, "分片特定加密密钥下载成功 (URI: ${segmentKeyInfo.uri}, 大小: ${segmentSpecificKeyBytes?.size ?: 0} bytes)")
                        } catch (ske: Exception) {
                            Log.e(TAG, "下载分片特定加密密钥失败 (URI: ${segmentKeyInfo.uri})", ske)
                            throw RuntimeException("下载分片特定加密密钥失败: ${ske.message}", ske)
                        }
                    } else if (globalEncryptionKeyInfo != null && segmentKeyInfo.uri == globalEncryptionKeyInfo.uri) {
                        segmentSpecificKeyBytes = downloadedKeyBytes 
                    }
                }
                val keyBytesForDecryption = segmentSpecificKeyBytes ?: downloadedKeyBytes

                val success = downloadSegment(
                    segment.uri,
                    segmentFile,
                    effectiveKeyInfoForSegment,
                    null, 
                    keyBytesForDecryption
                )

                if (success) {
                    downloadedSegmentFiles.add(segmentFile)
                    downloadedSegmentsCount++
                    currentTask = currentTask.copy(已下载大小 = downloadedSegmentsCount.toLong())
                    任务状态流.value = currentTask
                    Log.d(TAG, "已下载并处理分片 $downloadedSegmentsCount / ${tsSegments.size} for ${currentTask.文件名} (${segmentFile.name})")
                } else {
                    throw RuntimeException("分片 ${segment.uri} 处理失败。")
                }
            }
            Log.i(TAG, "所有分片下载并初步处理完成 (共 $downloadedSegmentsCount 个)")

            if (downloadedSegmentFiles.size != tsSegments.size) {
                 throw IllegalStateException("处理的分片数量 (${downloadedSegmentFiles.size}) 与M3U8中的分片数量 (${tsSegments.size}) 不匹配，无法合并。")
            }

            Log.d(TAG, "步骤5: 开始合并TS分片 (共 ${downloadedSegmentFiles.size} 个)")
            
            val finalOutputFile = File(currentTask.目标文件夹, currentTask.文件名)
            mergeSegments(downloadedSegmentFiles, finalOutputFile)

            currentTask = currentTask.copy(状态 = 下载状态.已完成)
            Log.i(TAG, "M3U8下载并合并完成: ${finalOutputFile.absolutePath}")

            // 步骤6: 清理临时文件 (在成功合并后执行)
            if (segmentsDir.exists()) {
                Log.d(TAG, "步骤6: 清理临时分片目录 ${segmentsDir.path}")
                try {
                    if (segmentsDir.deleteRecursively()) {
                        Log.i(TAG, "临时分片目录清理成功: ${segmentsDir.path}")
                    } else {
                        Log.w(TAG, "临时分片目录清理失败 (可能部分文件未删除): ${segmentsDir.path}")
                    }
                } catch (e: Exception) {
                    Log.e(TAG, "清理临时分片目录时发生异常: ${segmentsDir.path}", e)
                }
            }

        } catch (e: CancellationException) {
            Log.i(TAG, "M3U8下载被取消: ${currentTask.文件名}", e)
            currentTask = currentTask.copy(状态 = 下载状态.已取消, 错误信息 = "下载被取消")
             // 如果取消，也尝试清理临时目录
            if (segmentsDir.exists()) {
                Log.d(TAG, "步骤6 (取消时): 清理临时分片目录 ${segmentsDir.path}")
                segmentsDir.deleteRecursively()
            }
        } catch (e: Exception) {
            currentCoroutineContext().ensureActive()
            Log.e(TAG, "M3U8下载异常: ${currentTask.文件名}, ${e.message}", e)
            currentTask = currentTask.copy(状态 = 下载状态.失败, 错误信息 = e.localizedMessage ?: "M3U8下载未知错误")
            // 如果失败，也尝试清理临时目录
            if (segmentsDir.exists()) {
                Log.d(TAG, "步骤6 (失败时): 清理临时分片目录 ${segmentsDir.path}")
                segmentsDir.deleteRecursively()
            }
        } finally {
            任务状态流.value = currentTask
            // The main cleanup is now handled within try or specific catch blocks for completion/cancellation/failure.
            // The finally block here ensures the task status is emitted.
            // We might remove redundant cleanup checks from here if handled deterministically above.
            Log.d(TAG, "M3U8下载结束: ${currentTask.文件名}, 最终状态: ${currentTask.状态}")
        }
    }

} 